//
package org.whyr.android.player;

import android.app.Service;
//import android.content.BroadcastReceiver;
//import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.MediaPlayer;
import android.media.AudioManager;
import android.media.MediaPlayer.OnInfoListener;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.support.v4.content.LocalBroadcastManager;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.util.Log;
//import android.net.ConnectivityManager; 
//import android.net.NetworkInfo;


public class PlayerService extends Service  implements
MediaPlayer.OnCompletionListener, MediaPlayer.OnPreparedListener,
MediaPlayer.OnErrorListener, OnInfoListener{
	
	private static final String SERVICE_PREFIX = "org.whyr.android.player."; 
	private static final String SERVICE_PLAY = SERVICE_PREFIX + "PLAY"; 
	private static final String SERVICE_STOP = SERVICE_PREFIX + "STOP"; 
	private static final String SERVICE_PAUSE = SERVICE_PREFIX + "PAUSE";
	private static final String SERVICE_GET_STATUS = SERVICE_PREFIX + "GET_STATUS"; 
	
	private static final int MAX_CONNECTION_RETRIES = 5; 
	private int connectionRetries = 0; 
	
	private static MediaPlayer mPlayer = null; 
	public enum State{stoppingWhilePreparing, preparedAndStopping, stopped, preparing, playing, paused, pausedInCall, error}; 
	private State playerState = State.stopped; 
	private PlayerNotification notification;
	private TelephonyManager telephonyManager; 
	private PhoneStateListener listener;  
//	private NetworkInfo mNetworkInfo; 
	private String textMessage = "ready";
// connectivity receiver may by used in a later version to  monitor connectivity 
// state in order to conserve mobile bandwith (ie stopping paused streams after 
// certain time/  warn user that they are using mobile data) 
//	private BroadcastReceiver mConnectivityReceiver = new BroadcastReceiver(){
//		 @Override
//		 public void onReceive(Context c, Intent i){
//			 Log.d("debug", "connectivity change"); 
//			 checkConnection(); 
//		 }
//	 }; 
	 
//	 private void checkConnection(){
//		 ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
//		 mNetworkInfo = cm.getActiveNetworkInfo();
//		 if (mNetworkInfo == null){
//			 Log.d("debug", "no internet connection");
//		 }
//		 else{
//			 Log.d("debug", mNetworkInfo.toString());
//		 }
//	 }
	
	private void outputText(String s){
		Intent intent = new Intent(this, MainActivity.class); 
		intent.setAction("player-status-message");
		intent.putExtra("playerStatus", s);
		intent.putExtra("playerState", playerState);  
		LocalBroadcastManager.getInstance(this).sendBroadcast(intent); 
		notification.setText(s); 
		textMessage = s;  
		Log.d("debug", "outputText:" + s); 
	}
    public class MediaPlayerBinder extends Binder {
    	PlayerService getService() {
            return PlayerService.this;
        }
    }
	private final IBinder mBinder = new MediaPlayerBinder();
	
	private String TAG = getClass().getSimpleName();
	 @Override
	    public IBinder onBind(Intent intent) {
	        return mBinder;
	    }
	 @Override 
	 public int onStartCommand(Intent i, int a, int b){
		 Log.d("debug", "onStartCommand"); 
		 String s = i.getAction();
		 if (s.equals(SERVICE_PLAY)) play(); 
		 else if (s.equals(SERVICE_STOP)) stop(); 
		 else if (s.equals(SERVICE_PAUSE)) pause();
		 else if (s.equals(SERVICE_GET_STATUS)) getStatus(); 
		 return 0; 
	 }
	 @Override
	 public void onCreate(){
		super.onCreate(); 
		IntentFilter filter = new IntentFilter(); 
		filter.addAction("android.net.conn.CONNECTIVITY_CHANGE"); 
//		registerReceiver(mConnectivityReceiver, filter); 
		notification = new PlayerNotification(this); 
		telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
		// Create a PhoneStateListener to watch for off-hook and idle events
		listener = new PhoneStateListener() {
		  public void onCallStateChanged(int state, String incomingNumber) {
		    switch (state) {
		      case TelephonyManager.CALL_STATE_OFFHOOK:
		      case TelephonyManager.CALL_STATE_RINGING:
		        // Phone going off-hook or ringing, pause the player.
		        if (playerState == State.playing) {
		          mPlayer.pause();
		          playerState = State.pausedInCall;
		        }
		        break;
		      case TelephonyManager.CALL_STATE_IDLE:
		        if (playerState == State.pausedInCall) {
		          play();
		        }
		        break;
		    }
		  }
		};

		telephonyManager.listen(listener, PhoneStateListener.LISTEN_CALL_STATE);
	 }
	 public PlayerService(){
		 init(); 
	 }
	 private void retry(){
		 
		 if (connectionRetries > MAX_CONNECTION_RETRIES - 1) {
			 playerState = State.stopped; 
			 outputText("unable to reconnect after " + MAX_CONNECTION_RETRIES + " attempts.  Check internet connection");
			 stopSelf(); 
		 }
		
		else{
			 Log.d("debug", "retrying!");
			 outputText("attempting to reconnect...");
			 Handler handler = new Handler(); 
			 handler.postDelayed(new Runnable(){
		     public void run(){
		    	 if (playerState == State.error){
			    	 mPlayer.reset();
			    	 init();
			    	 play(); 
					 connectionRetries++; 
					 
		    	 }
			  }
		  }, 2000);
		}
	 }
	 private void init(){
		 mPlayer = new MediaPlayer();
		 mPlayer.setOnCompletionListener(this);
		 mPlayer.setOnErrorListener(this);
		 mPlayer.setOnInfoListener(this);
		 mPlayer.setOnPreparedListener(this);
		 try{
			 mPlayer.setDataSource("http://184.154.202.243:8061/;"); 
		 }catch (Throwable e){
			Log.d(TAG + " in init", e.toString()); 
		 } 
		 mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
		 playerState = State.stopped; 
	 }
	 	
	 private synchronized void getStatus(){
		outputText(textMessage);  
	 }
	 public synchronized void play() {
		 if (playerState == State.stopped){
			 	playerState = State.preparing; 
			 	//statusText.setText("preparing stream...");
			 	outputText("preparing stream..."); 
			 	notification.play(); 
			 	mPlayer.prepareAsync();
		 }
		 else if (playerState == State.paused || playerState==State.pausedInCall){
			 playerState = State.playing; 
			 mPlayer.start();
			 outputText("playing..."); 
			 notification.play(); 			  
		 }
		 else if (playerState == State.stoppingWhilePreparing){
			 playerState = State.preparing; 
			 outputText("preparing stream...");
		 }
	 }
		 @Override 
		 public void onPrepared(MediaPlayer mp) { 
			 if (playerState == State.preparing){
				 playerState = State.playing; 
				 mp.start();
				 outputText("playing...");
				 notification.play(); 
				 connectionRetries = 0;
			 }
			 if (playerState == State.stoppingWhilePreparing){
				 playerState = State.preparedAndStopping; 
				 stop(); 
			 }
		 }

		 public synchronized void pause() {
		  if (playerState == State.playing){ 
			  playerState = State.paused; 
			  mPlayer.pause();
			  outputText("paused");
			  notification.pause(); 
		  }
		 }
		 public synchronized void stop() {
		  if (playerState == State.playing || playerState == State.paused || playerState == State.preparedAndStopping){ 
			  playerState = State.stopped; 
			  mPlayer.stop();
			  outputText("stopped"); 
			  notification.hide();
			  stopSelf(); 
		  }
		  else if (playerState == State.preparing){
			  playerState = State.stoppingWhilePreparing; 
			  outputText("stopping..."); 
		  }
		  else if (playerState == State.error){
			 //mPlayer.reset(); 
			 //init();
			 playerState = State.stopped; 
			 outputText("stopped.");
			 stopSelf(); 
		  }
		 
		 }
		 @Override
		 public void onDestroy() {
//		  unregisterReceiver(mConnectivityReceiver); 
		  super.onDestroy();
		 }

		 public void onCompletion(MediaPlayer mp) {
			 Log.d("debug", "onCompletion!!"); 
		 }

		 public boolean onError(MediaPlayer mp, int what, int extra) {
			 connectionError();
			 return true;
		 }
		@Override
		public boolean onInfo(MediaPlayer arg0, int info, int arg2) {
			//from testing, onInfo is only called when stream is interrupted, might as well reset stream
			//downside is losing buffered stream from pausing
			connectionError();
			return false;
		}

		private void connectionError() {	
			  playerState = State.error; 
		      outputText("connection error");  
			  Handler handler = new Handler(); 
			  handler.postDelayed(new Runnable(){
				  public void run(){
					  if (playerState == State.error){
						  retry();
					  }
				  }
			  }, 2000);
	
		}
}
